﻿namespace JanusLibAing
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Text;
    using System.Windows.Forms;
    using MultiLang;

    /// <summary>
    /// Prompts the user to select a folder.
    /// Author - Vladek from rsdn.ru
    /// Modified by Alex Nek
    /// </summary>
    public sealed class FolderBrowserDialog : CommonDialog
    {
        #region EFolderBrowserRootType enum

        /// <summary>
        ///   Specifies the type of the root folder in a <see cref = "FolderBrowserDialog" />.
        /// </summary>
        public enum EFolderBrowserRootType
        {
            /// <summary>
            ///   Use <see cref = "System.Environment.SpecialFolder" />.
            /// </summary>
            SpecialFolder,

            /// <summary>
            ///   Use a path.
            /// </summary>
            Path,
        }

        #endregion

        private NativeMethods.FolderBrowserOptions dialogOptions;
        private string rootPath;
        private Environment.SpecialFolder rootSpecialFolder = Environment.SpecialFolder.Desktop;
        private EFolderBrowserRootType rootType = EFolderBrowserRootType.SpecialFolder;
        private string initPath;
        private string selectedPath;
        private string title = ml.ml_string(1, "Выберите каталог");
        private string description;

        /// <summary>
        ///   Initializes a new instance of the <see cref = "FolderBrowserDialog" /> class.
        /// </summary>
        public FolderBrowserDialog()
        {
            Initialize();
        }

        #region Methods

        [DllImport("user32.dll", EntryPoint = "SetWindowText", CharSet = CharSet.Unicode)]
        public static extern bool SetWindowText(IntPtr hWnd, String strNewWindowName);

        /// <summary>
        ///   Resets the properties of a common dialog to their default values.
        /// </summary>
        public override void Reset()
        {
            Initialize();
        }

        /// <summary>
        ///   Displays the folder browser dialog.
        /// </summary>
        /// <param name = "hwndOwner">Handle to the window that owns the dialog box.</param>
        /// <returns>
        ///   If the user clicks the OK button of the dialog that is displayed, true is returned; otherwise, false.
        /// </returns>
        protected override bool RunDialog(IntPtr hwndOwner)
        {
            bool result = false;

            IntPtr pidlRoot = IntPtr.Zero,
                   pidlSelected = IntPtr.Zero;
            StringBuilder pszPath = new StringBuilder(NativeMethods.MAX_PATH);

            // SelectedPath = string.Empty;

            try
            {
                if (RootType == EFolderBrowserRootType.SpecialFolder)
                {
                    NativeMethods.SHGetFolderLocation(hwndOwner, (int)RootSpecialFolder, IntPtr.Zero, 0, out pidlRoot);
                }
                else
                {
                    // RootType == Path
                    uint iAttribute;
                    NativeMethods.SHParseDisplayName(RootPath, IntPtr.Zero, out pidlRoot, 0, out iAttribute);
                }

                NativeMethods.BROWSEINFO browseInfo = new NativeMethods.BROWSEINFO();
                browseInfo.HwndOwner = hwndOwner;
                browseInfo.Root = pidlRoot;
                browseInfo.DisplayName = new string(' ', 256);
                browseInfo.Title = Description;
                browseInfo.Flags = (uint)dialogOptions;
                browseInfo.LParam = 0;
                browseInfo.Callback = BrowseCallbackProc;

                // Show dialog
                pidlSelected = NativeMethods.SHBrowseForFolder(ref browseInfo);

                if (pidlSelected != IntPtr.Zero)
                {
                    result = true;

                    NativeMethods.SHGetPathFromIDList(pidlSelected, pszPath);

                    SelectedPath = pszPath.ToString();
                }
            }
            finally
            {
                // release all unmanaged resources
                NativeMethods.IMalloc malloc = NativeMethods.GetSHMalloc();

                if (pidlRoot != IntPtr.Zero)
                {
                    malloc.Free(pidlRoot);
                }

                if (pidlSelected != IntPtr.Zero)
                {
                    malloc.Free(pidlSelected);
                }

                Marshal.ReleaseComObject(malloc);
            }

            return result;
        }

        private void Initialize()
        {
            RootType = EFolderBrowserRootType.SpecialFolder;
            RootSpecialFolder = Environment.SpecialFolder.Desktop;
            RootPath = string.Empty;
            Description = string.Empty;

            // default options
            dialogOptions = NativeMethods.FolderBrowserOptions.BrowseFiles
                            | NativeMethods.FolderBrowserOptions.ShowEditBox
                            | NativeMethods.FolderBrowserOptions.UseNewStyle
                            | NativeMethods.FolderBrowserOptions.BrowseShares
                            | NativeMethods.FolderBrowserOptions.ShowStatusText
                            | NativeMethods.FolderBrowserOptions.ValidateResult;
        }

        private bool GetOption(NativeMethods.FolderBrowserOptions option)
        {
            return (dialogOptions & option) != NativeMethods.FolderBrowserOptions.None;
        }

        [SecurityCritical]
        private void SetOption(NativeMethods.FolderBrowserOptions option, bool value)
        {
            if (value)
            {
                dialogOptions |= option;
            }
            else
            {
                dialogOptions &= ~option;
            }
        }

        private IntPtr BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData)
        {
#if DEBUG && TRACE
            Trace.TraceInformation("BrowseCallbackProc(msg={0}, lParam={1}, lpData={2})", msg, lParam, lpData); //MLHIDE
#endif

#if true
            FolderBrowserEventArgs args = new FolderBrowserEventArgs();
            StringBuilder pszPath = new StringBuilder(NativeMethods.MAX_PATH);

            switch (msg)
            {
                case NativeMethods.BFFM_INITIALIZED:
                    args.SelectedPath = InitPath;
                    SelectedPath = String.Empty;
                    OnInitialized(args);
                    SetWindowText(hwnd, Title);
                    break;

                case NativeMethods.BFFM_SELCHANGED:
                    NativeMethods.SHGetPathFromIDList(lParam, pszPath);

                    SelectedPath = pszPath.ToString();
#if DEBUG && TRACE
                    Trace.TraceInformation("SelectedPath={0}", SelectedPath); //MLHIDE
#endif

                    args.SelectedPath = SelectedPath;
                    OnSelectionChanged(args);
                    break;

                case NativeMethods.BFFM_VALIDATEFAILEDW:
                    return new IntPtr(1);

                default:
                    return IntPtr.Zero;
            }


            if (args.OkButtonIsEnabled.HasValue)
            {
                NativeMethods.SendMessage(
                    hwnd, NativeMethods.BFFM_ENABLEOK, IntPtr.Zero, new IntPtr(args.OkButtonIsEnabled.Value ? 1 : 0));
            }

            if (!String.IsNullOrEmpty(args.OkButtonText))
            {
                NativeMethods.SendMessage(hwnd, NativeMethods.BFFM_SETOKTEXT, IntPtr.Zero, args.OkButtonText);
            }

            if (!String.IsNullOrEmpty(args.ExpandedPath))
            {
                NativeMethods.SendMessage(hwnd, NativeMethods.BFFM_SETEXPANDED, new IntPtr(1), args.ExpandedPath);
            }

            if (!String.IsNullOrEmpty(args.SelectedPath) && args.SelectedPath != SelectedPath)
            {
#if DEBUG && TRACE
                Trace.TraceInformation("args.SelectedPath={0}", args.SelectedPath); //MLHIDE
                Trace.TraceInformation("_SelectedPath={0}", SelectedPath); //MLHIDE
#endif
                NativeMethods.SendMessage(hwnd, NativeMethods.BFFM_SETSELECTIONW, new IntPtr(1), args.SelectedPath);
            }

            if (!String.IsNullOrEmpty(args.StatusText))
            {
                NativeMethods.SendMessage(hwnd, NativeMethods.BFFM_SETSTATUSTEXTW, new IntPtr(1), args.StatusText);
            }

#endif
            return HookProc(hwnd, msg, lParam, lpData);
        }

        private void OnInitialized(FolderBrowserEventArgs e)
        {
            e.ExpandedPath = e.SelectedPath;
            //e.SelectedPath = SelectedPath;
            EventHandler<FolderBrowserEventArgs> handler = Initialized;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void OnSelectionChanged(FolderBrowserEventArgs e)
        {
            EventHandler<FolderBrowserEventArgs> handler = SelectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region Properties


        /// <summary>
        ///   Gets or sets the type of the root.
        /// </summary>
        /// <value>The type of the root.</value>
        public EFolderBrowserRootType RootType
        {
            get { return rootType; }
            set { rootType = value; }
        }

        /// <summary>
        ///   Gets or sets the root path.
        ///   <remarks>
        ///     Valid only if RootType is set to Path.
        ///   </remarks>
        /// </summary>
        /// <value>The root path.</value>
        public string RootPath
        {
            get { return rootPath; }
            set
            {
                rootPath = value;
                RootType = EFolderBrowserRootType.Path;
            }
        }

        /// <summary>
        ///   Gets or sets the root special folder.
        ///   <remarks>
        ///     Valid only if RootType is set to SpecialFolder.
        ///   </remarks>
        /// </summary>
        /// <value>The root special folder.</value>
        public Environment.SpecialFolder RootSpecialFolder
        {
            get { return rootSpecialFolder; }
            set
            {
                rootSpecialFolder = value;
                RootType = EFolderBrowserRootType.SpecialFolder;
            }
        }

        /// <summary>
        ///   Gets or sets the display name.
        /// </summary>
        /// <value>The display name.</value>
        public string SelectedPath
        {
            get
            {
                return selectedPath;
            }

            set
            {
                selectedPath = value;
            }
        }

        /// <summary>
        ///   Gets or sets the description.
        /// </summary>
        /// <value>The title.</value>
        public string Description
        {
            get { return description; }
            set { description = value; }
        }

        /// <summary>
        ///   Gets or sets a value indicating whether browsing files is allowed.
        /// </summary>
        /// <value></value>
        public bool BrowseFiles
        {
            get { return GetOption(NativeMethods.FolderBrowserOptions.BrowseFiles); }
            set { SetOption(NativeMethods.FolderBrowserOptions.BrowseFiles, value); }
        }

        /// <summary>
        ///   Gets or sets a value indicating whether to show an edit box.
        /// </summary>
        /// <value></value>
        public bool ShowEditBox
        {
            get { return GetOption(NativeMethods.FolderBrowserOptions.ShowEditBox); }
            set { SetOption(NativeMethods.FolderBrowserOptions.ShowEditBox, value); }
        }

        /// <summary>
        ///   Gets or sets a value indicating whether browsing shares is allowed.
        /// </summary>
        /// <value></value>
        public bool BrowseShares
        {
            get { return GetOption(NativeMethods.FolderBrowserOptions.BrowseShares); }
            set { SetOption(NativeMethods.FolderBrowserOptions.BrowseShares, value); }
        }

        /// <summary>
        ///   Gets or sets a value indicating whether to show status text.
        /// </summary>
        /// <value></value>
        public bool ShowStatusText
        {
            get { return GetOption(NativeMethods.FolderBrowserOptions.ShowStatusText); }
            set { SetOption(NativeMethods.FolderBrowserOptions.ShowStatusText, value); }
        }

        /// <summary>
        ///   Gets or sets a value indicating whether to validate the result.
        /// </summary>
        /// <value></value>
        public bool ValidateResult
        {
            get { return GetOption(NativeMethods.FolderBrowserOptions.ValidateResult); }
            set { SetOption(NativeMethods.FolderBrowserOptions.ValidateResult, value); }
        }

        public bool ShowNewFolderButton
        {
            get { return !GetOption(NativeMethods.FolderBrowserOptions.HideNewFolderButton); }
            set { SetOption(NativeMethods.FolderBrowserOptions.HideNewFolderButton, !value); }
        }

        public string InitPath
        {
            get { return initPath; }
            set { initPath = value; }
        }

        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        #endregion

        #region Events

        public event EventHandler<FolderBrowserEventArgs> Initialized;
        public event EventHandler<FolderBrowserEventArgs> SelectionChanged;

        #endregion
    }
}